﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using ScrumTable.BL.DM.Properties;
using ScrumTable.Common;
using ScrumTable.DL.Data.Schema;

#endregion

namespace ScrumTable.BL.DM.DataManagement
{
    /// <summary>
    /// Specifies the mapping between the property names of a DomainObject
    /// and the Schema.
    /// </summary>
    public class DomainSchemaMapper
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private readonly IDictionary<string, Mapping> _schemaToPropertyMapping;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the property name associated with the schema.
        /// </summary>
        /// <param name="schema">Specifies the schema to map to a property.</param>
        public string this[string schema]
        {
            get
            {
                PreCondition.AssertNotNullOrEmpty(schema, "schema");
                EnsureSchemaAvailable(schema);
                return _schemaToPropertyMapping[schema].PropertyName;
            }
        }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new SchemaMapper instance, no public constructor
        /// needed.
        /// </summary>
        public DomainSchemaMapper()
        {
            _schemaToPropertyMapping = new Dictionary<string, Mapping>();
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new PropertyMapping instance, no public constructor
        /// needed.
        /// </summary>
        /// <param name="schema">Specifies the associated schema instance.</param>
        /// <param name="propertyName">Specifies the property name. This is required because of update events from the back end system.</param>
        /// <param name="canUndoAction">Specifies that the property assigend to the given schema can be undo. Null means the property can't be undo.</param>
        public void Add(SchemaBase schema, string propertyName, Func<object, object, bool> canUndoAction = null)
        {
            PreCondition.AssertNotNull(schema, "schema");
            PreCondition.AssertNotNullOrEmpty(propertyName, "propertyName");

            _schemaToPropertyMapping[schema.SchemaName] = new Mapping
                                                              {
                                                                  CanUndoAction = canUndoAction,
                                                                  PropertyName = propertyName
                                                              };
        }

        /// <summary>
        /// Creates a new PropertyMapping instance, no public constructor
        /// needed.
        /// </summary>
        /// <param name="schema">Specifies the associated schema instance.</param>
        /// <param name="propertyName">Specifies the property name. This is required because of update events from the back end system.</param>
        /// <param name="canUndo">Specifies that the property assigend to the given schema can be undo. Null means the property can't be undo.</param>
        public void Add(SchemaBase schema, string propertyName, bool canUndo)
        {
            Add(
                schema,
                propertyName,
                canUndo ? (originalVal, newVal) => true : (Func<object, object, bool>)null);
        }

        /// <summary>
        /// Returns true if the current mapper contains a mapping for the given schema field.
        /// </summary>
        /// <param name="schema">Specifies the name of the schema field.</param>
        public bool ContainsSchema(string schema)
        {
            return _schemaToPropertyMapping.ContainsKey(schema);
        }

        /// <summary>
        /// Returns true if the mapped property value can be undo.
        /// </summary>
        /// <param name="schema">Specifies the name of the schema field.</param>
        /// <param name="currentValue">Specifies the current value of the field.</param>
        /// <param name="newValue">Specifies the new value of the field.</param>
        public bool CanUndo(SchemaBase schema, object currentValue, object newValue)
        {
            EnsureSchemaAvailable(schema);

            return (_schemaToPropertyMapping[schema].CanUndoAction != null
                && _schemaToPropertyMapping[schema].CanUndoAction(currentValue, newValue));
        }

        private void EnsureSchemaAvailable(string schema)
        {
            if (!ContainsSchema(schema))
            {
                throw new InvalidOperationException(string.Format(Resources.ExcDmnMapperNotDefined, schema));
            }
        }

        #endregion


        //--------------------------------------------------------------------
        // Nested Classes
        //--------------------------------------------------------------------

        /// <summary>
        /// Contains the mapping information.
        /// </summary>
        private class Mapping
        {
            #region Properties
            //--------------------------------------------------------------------
            // Properties
            //--------------------------------------------------------------------

            internal Func<object, object, bool> CanUndoAction { get; set; }
            internal string PropertyName { get; set; }

            #endregion
        }
    }
}